package crypto
{
	import com.hurlant.crypto.rsa.RSAKey;
	import com.hurlant.math.BigInteger;
	import com.hurlant.util.Base64;
	import com.hurlant.util.der.OID;

	import flash.utils.ByteArray;


	public final class RSAKeyGenerate
	{

		private static const OBJECT_IDENTIFIER_TYPE_CODE:int = 0x06;

		private static const RSA_IDENTIFIER_LENGTH:int = 9;

		private static const SEQUENCE_TYPE_CODE:int = 0x30;

		private static const INTEGER_TYPE_CODE:int = 0x02;

		private static const NULL_TYPE_CODE:int = 0x05;

		private static const NULL_VALUE:int = 0x00;

		private static const BIT_STRING_TYPE_CODE:int = 0x03;

		private static const BIT_STRING_TAG:int = 0x00;

		public static function getPublicKey(rsaKey:RSAKey):ByteArray
		{
			var nArr:ByteArray = parseInteger(rsaKey.n.toByteArray());
			var eArr:ByteArray = parseInteger(new BigInteger(rsaKey.e.toString(16), 16).toByteArray());
			var valueBytes:ByteArray = new ByteArray();
			valueBytes.position = 0;
			valueBytes.writeBytes(nArr);
			valueBytes.writeBytes(eArr);
			valueBytes = parseSequence(valueBytes);
			valueBytes = parseBitString(valueBytes);
			var identifyBytes:ByteArray = getRSAIdentifierBytes();
			var resultBytes:ByteArray = new ByteArray();
			resultBytes.writeBytes(identifyBytes, 0, identifyBytes.length);
			resultBytes.writeBytes(valueBytes, 0, valueBytes.length);
			resultBytes = parseSequence(resultBytes);
			resultBytes.position = 0;
			var completeKey:String = MahjongPEM.writePubickeyToCert(resultBytes);
			var bytes:ByteArray = new ByteArray();
			bytes.writeUTFBytes(completeKey);
			return bytes;
		}

		private static function getRSAIdentifierBytes():ByteArray
		{
			var objectIdentifier:MahjongObjectIdentifier = new MahjongObjectIdentifier(OBJECT_IDENTIFIER_TYPE_CODE, RSA_IDENTIFIER_LENGTH, OID.RSA_ENCRYPTION);
			var bytes:ByteArray = objectIdentifier.toDER();
			bytes.position = bytes.length;
			bytes.writeByte(NULL_TYPE_CODE);
			bytes.writeByte(NULL_VALUE);
			return parseSequence(bytes);
		}

		private static function getLengthHeader(len:int):ByteArray
		{
			var lengthBytes:ByteArray = new ByteArray();
			if (len < 128)
			{
				lengthBytes.writeByte(len);
			}
			else if (len < 256)
			{
				lengthBytes.writeByte(1 | 0x80);
				lengthBytes.writeByte(len);
			}
			else if (len < 65536)
			{
				lengthBytes.writeByte(2 | 0x80);
				lengthBytes.writeByte(len >> 8);
				lengthBytes.writeByte(len);
			}
			else if (len < 65536 * 256)
			{
				lengthBytes.writeByte(3 | 0x80);
				lengthBytes.writeByte(len >> 16);
				lengthBytes.writeByte(len >> 8);
				lengthBytes.writeByte(len);
			}
			else
			{
				lengthBytes.writeByte(4 | 0x80);
				lengthBytes.writeByte(len >> 24);
				lengthBytes.writeByte(len >> 16);
				lengthBytes.writeByte(len >> 8);
				lengthBytes.writeByte(len);
			}
			lengthBytes.position = 0;
			return lengthBytes;
		}

		private static function parseInteger(bytes:ByteArray):ByteArray
		{
			var integerBytes:ByteArray = new ByteArray();
			integerBytes.position = 0;
			integerBytes.writeByte(INTEGER_TYPE_CODE);
			var lengthBytes:ByteArray = getLengthHeader(bytes.length);
			integerBytes.writeBytes(lengthBytes, 0, lengthBytes.length);
			integerBytes.writeBytes(bytes, 0, bytes.length);
			integerBytes.position = 0;
			return integerBytes;
		}

		private static function parseSequence(bytes:ByteArray):ByteArray
		{
			var sequenceBytes:ByteArray = new ByteArray();
			sequenceBytes.position = 0;
			sequenceBytes.writeByte(SEQUENCE_TYPE_CODE);
			var lengthBytes:ByteArray = getLengthHeader(bytes.length);
			sequenceBytes.writeBytes(lengthBytes, 0, lengthBytes.length);
			sequenceBytes.writeBytes(bytes, 0, bytes.length);
			sequenceBytes.position = 0;
			return sequenceBytes;
		}

		private static function parseBitString(bytes:ByteArray):ByteArray
		{
			var bitStringBytes:ByteArray = new ByteArray();
			bitStringBytes.position = 0;
			bitStringBytes.writeByte(BIT_STRING_TYPE_CODE);
			var lengthBytes:ByteArray = getLengthHeader(bytes.length + 1);
			bitStringBytes.writeBytes(lengthBytes, 0, lengthBytes.length);
			bitStringBytes.writeByte(BIT_STRING_TAG);
			bitStringBytes.writeBytes(bytes, 0, bytes.length);
			bitStringBytes.position = 0;
			return bitStringBytes;
		}
	}
}
